package top.lingkang.finaloauth2.client.http;

import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import top.lingkang.finaloauth2.client.config.ResourceServerProperties;
import top.lingkang.finaloauth2.client.error.BaseClientException;
import top.lingkang.finaloauth2.client.error.TokenInvalidException;
import top.lingkang.finaloauth2.constants.ClientConstants;
import top.lingkang.finaloauth2.entity.AccessToken;
import top.lingkang.finaloauth2.entity.OAuth2AccessToken;
import top.lingkang.finaloauth2.utils.StringUtils;

import java.util.HashMap;
import java.util.Set;

/**
 * @author lingkang
 * Created by 2022/3/23
 */
public class ResourceServerHolderServices implements ResourceServerHolder {
    private static final Log log = LogFactory.getLog(ResourceServerHolderServices.class);

    private String serverUrl;

    public ResourceServerHolderServices(String serverUrl) {
        this.serverUrl = serverUrl;
    }

    @Autowired(required = false)
    private ResourceHttpAccess resourceHttpAccess;
    @Autowired(required = false)
    private ResourceServerGetToken resourceServerGetToken;
    @Autowired
    private ResourceServerProperties resourceServerProperties;

    @Override
    public JSONObject login(String username, String password) {
        Assert.notBlank(username, "username不能为空！");
        Assert.notBlank(password, "password不能为空！");
        HashMap<String, Object> param = new HashMap<>();
        param.put("client_id", resourceServerProperties.getClientId());
        param.put("client_secret", resourceServerProperties.getClientSecret());
        param.put("grant_type", "password");
        param.put("username", username);
        param.put("password", password);
        String result = resourceHttpAccess.post(
                serverUrl + "/oauth2/token",
                null, param);
        JSONObject json = JSONObject.parseObject(result);
        if (json.getInteger("code") == 0) {
            return json;
        } else {
            throw new BaseClientException(json.getString("msg"));
        }
    }

    @Override
    public JSONObject refreshToken(String refreshToken) {
        Assert.notBlank(refreshToken, "refreshToken不能为空！");
        HashMap<String, Object> param = new HashMap<>();
        param.put("client_id", resourceServerProperties.getClientId());
        param.put("client_secret", resourceServerProperties.getClientSecret());
        param.put("grant_type", "refresh_token");
        param.put("refresh_token", refreshToken);
        String result = resourceHttpAccess.post(
                serverUrl + "/oauth2/token",
                null, param);
        JSONObject json = JSONObject.parseObject(result);
        if (json.getInteger("code") == 0) {
            return json;
        } else {
            throw new BaseClientException(json.getString("msg"));
        }
    }

    @Override
    public JSONObject logout(String token) {
        Assert.notBlank(token, "refreshToken不能为空！");
        HashMap<String, Object> param = new HashMap<>();
        param.put("access_token", token);
        String result = resourceHttpAccess.post(
                serverUrl + "/oauth2/logout",
                null, param);
        JSONObject json = JSONObject.parseObject(result);
        if (json.getInteger("code") == 0) {
            return json;
        } else {
            throw new BaseClientException(json.getString("msg"));
        }
    }

    @Override
    public Set<String> getRole() {
        Set<String> authority = ResourceContextHolder.getRole();
        if (authority == null) {
            return authority();
        }
        return authority;
    }

    @Override
    public Set<String> getRole(boolean isNew) {
        if (isNew) {
            return authority();
        } else {
            Set<String> authority = ResourceContextHolder.getRole();
            if (authority == null) {
                return authority();
            }
            return authority;
        }
    }

    private Set<String> authority() {
        String token = resourceServerGetToken.getToken(ResourceContextHolder.getRequest());
        if (StringUtils.isEmpty(token)) {
            throw new TokenInvalidException(ClientConstants.INVALID_TOKEN);
        }
        String result = resourceHttpAccess.get(
                serverUrl + "/oauth2/authority?access_token=" + token,
                null);
        if (!StringUtils.isEmpty(result)) {
            Set set = JSON.parseObject(result, Set.class);
            ResourceContextHolder.setRole(set);
            return set;
        }
        throw new TokenInvalidException(ClientConstants.INVALID_TOKEN);
    }

    @Override
    public AccessToken getAccessToken() {
        AccessToken accessToken = ResourceContextHolder.getAccessToken();
        if (accessToken != null) {
            return accessToken;
        }
        String token = resourceServerGetToken.getToken(ResourceContextHolder.getRequest());
        if (StringUtils.isEmpty(token)) {
            throw new TokenInvalidException(ClientConstants.INVALID_TOKEN);
        }
        String result = resourceHttpAccess.get(
                serverUrl + "/oauth2/info?access_token=" + token,
                null);
        if (!StringUtils.isEmpty(result)) {
            accessToken = JSON.parseObject(result, AccessToken.class);
            if (accessToken.getCode() != 0) {
                throw new TokenInvalidException(ClientConstants.INVALID_TOKEN);
            }
            ResourceContextHolder.setAccessToken(accessToken);
            return accessToken;
        }
        throw new TokenInvalidException(ClientConstants.INVALID_TOKEN);
    }

    @Override
    public String generateAuthCode(String redirectUrl, String scope, String state) {
        Assert.notBlank(redirectUrl, "重定向url不能为空！");
        HashMap<String, Object> param = new HashMap<>();
        param.put("client_id", resourceServerProperties.getClientId());
        param.put("client_secret", resourceServerProperties.getClientSecret());
        param.put("response_type", "code");
        param.put("redirect_url", redirectUrl);
        param.put("scope", scope);
        param.put("state", state);
        String result = resourceHttpAccess.post(
                serverUrl + "/oauth2/authorize",
                null, param);
        JSONObject json = JSONObject.parseObject(result);
        if (json.getInteger("code") == 0) {
            return json.getString("authCode");
        } else {
            throw new BaseClientException(json.getString("msg"));
        }
    }

    @Override
    public OAuth2AccessToken authCodeGetToken(String code) {
        Assert.notBlank(code, "授权码不能为空！");
        HashMap<String, Object> param = new HashMap<>();
        param.put("client_id", resourceServerProperties.getClientId());
        param.put("client_secret", resourceServerProperties.getClientSecret());
        param.put("grant_type", "authorization_code");
        param.put("code", code);
        String result = resourceHttpAccess.post(
                serverUrl + "/oauth2/token",
                null, param);
        JSONObject json = JSONObject.parseObject(result);
        if (json.getInteger("code") == 0) {
            OAuth2AccessToken auth2AccessToken = JSONObject.parseObject(json.getString("data"), OAuth2AccessToken.class);
            return auth2AccessToken;
        } else {
            throw new BaseClientException(json.getString("msg"));
        }
    }
}
